/*
 * @Author: huangwei
 * @Date: 2021-05-14 17:13:42
 * @LastEditors: 黄威
 * @LastEditTime: 2022-02-15 15:50:35
 * @Description: 图层操作基类
 */
import type { BaseServices } from 'types'

import { Component } from 'vue-property-decorator'
import Vue from 'vue'

interface LayerManager<T, V> {
    createLayer(svcType: string, svcUrl: string, props?: any): Promise<V>

    addToMap(m: T, l: V): void
    removeFromMap(m: T, l: V): void

    moveLayer(m: T, l: V, l2?: V): void

    setOpacity(l: V, dOpa: number): void
    setVisable(l: V, isVisable: boolean): void
}

@Component
export default class BaseLayerMixin<T, V> extends Vue {
    map!: T

    // 图层操作器
    adapter!: LayerManager<T, V>

    // 所有的图层信息
    hashLayers: { [key: string]: V } = {}

    // 基础图层id
    baseIds: string[] = []

    // 返回图层数组
    get layers() {
        return Object.values(this.hashLayers)
    }

    /**
     * @public
     * 添加地图服务
     * @param {String} lyrId 图层id
     * @param {String} svcType 服务类型
     * @param {String} svcUrl 服务地址
     * @param {any?} props 服务元数据（不一定需要）
     * @returns 图层对象
     */
    async addService(
        lyrId: string,
        svcType: string,
        svcUrl: string,
        props?: any
    ): Promise<V> {
        if (this.hashLayers[lyrId]) throw '图层id重复或者图层已经被添加！'

        // 异步创建图层
        const layer = await this.adapter.createLayer(svcType, svcUrl, props)

        // 添加一些附加属性
        ;(layer as any).__layerId = lyrId
        ;(layer as any).__layerType = 'service'

        // 添加到图层中
        this.addLayer(lyrId, layer)

        // 返回创建的图层
        return layer
    }

    /**
     * @public
     * 移除地图服务
     * @param {String} lyrId
     */
    removeService(lyrId: string | V) {
        this.removeLayer(lyrId)
    }

    /**
     * @public
     * 获取图层
     * @param {String} lyrId 图层id或者图层
     * @returns 图层对象
     */
    getLayer(lyrId: string | V): V | undefined {
        if (typeof lyrId !== 'object') {
            return this.hashLayers[String(lyrId)]
        } else {
            return lyrId
        }
    }

    /**
     * @public
     * 添加图层
     * @param {String} lyrId 图层id
     * @param {any} lyrObj 图层参数
     */
    addLayer(lyrId: string, lyrObj: V) {
        if (this.hashLayers[lyrId]) {
            throw '图层id重复或者图层已经被添加！'
        }
        // map添加图层
        this.adapter.addToMap(this.map, lyrObj)
        this.$set(this.hashLayers, lyrId, lyrObj)

        /**
         * 图层新增
         * @property {string} lyrId 图层id
         * @property {any} lyrObj 图层对象
         */
        this.$emit('onLayerAdded', lyrId, lyrObj)

        return lyrObj
    }

    /**
     * @public
     * 移除图层
     * @param {String} lyrId
     * @returns
     */
    removeLayer(lyrId: string | V) {
        const lyrObj = this.getLayer(lyrId)
        if (!lyrObj) return

        // map移除图层
        this.adapter.removeFromMap(this.map, lyrObj)

        // 如果是string 尝试删除
        if (typeof lyrId === 'string') {
            this.$delete(this.hashLayers, lyrId)
        }

        /**
         * 图层移除
         * @property {string} lyrId 图层id
         * @property {any} lyrObj 图层对象
         */
        this.$emit('onLayerRemoved', lyrId, lyrObj)

        return lyrObj
    }

    /**
     * @public
     * 移动图层顺序
     * @param {String} layrId 要移动的图层id
     * @param {String} beforeId 移动到哪个图层上面（layerId 比 beforeId高一级）
     * @returns
     */
    moveLayer(layrId: string, beforeId: string) {
        const l1 = this.getLayer(layrId)
        const l2 = this.getLayer(beforeId)

        // 如果要移动的图层不存在
        if (!l1) return
        this.adapter.moveLayer(this.map, l1, l2)
    }

    /**
     * @public
     * 设置服务透明度
     * @param {String} lyrId 图层ID
     * @param {Number} dOpa 透明度
     * @returns
     */
    setLayerOpacity(lyrId: string | V, dOpa: number) {
        const lyrObj = this.getLayer(lyrId)
        if (!lyrObj) return

        this.adapter.setOpacity(lyrObj, dOpa)

        /**
         * 图层透明度修改
         * @property {string} lyrId 图层id
         * @property {number} dOpa 透明度
         */
        this.$emit('onLyrOpacityChanged', lyrId, dOpa)
    }

    /**
     * @public
     * 设置服务显隐
     * @param {String} lyrId 图层id
     * @param {Boolean} isVisable 显影
     * @returns
     */
    setLayerVisable(lyrId: string | V, isVisable: boolean) {
        const lyrObj = this.getLayer(lyrId)
        if (!lyrObj) return

        this.adapter.setVisable(lyrObj, isVisable)

        /**
         * 图层可见性修改
         * @property {string} lyrId 图层id
         * @property {boolean} isVisable 显影
         */
        this.$emit('onLyrVisableChanged', lyrId, isVisable)
    }

    /**
     * @public
     * 切换底图方案
     * @param baseServices 底图方案
     * @returns {Promise.<String[]>} 返回加载成果的图层id
     */
    async changeBaseServices(
        baseServices: BaseServices = []
    ): Promise<string[]> {
        // 删除图层
        this.baseIds.forEach(id => this.removeService(id))
        this.baseIds = []

        // 构建图层并添加到底图图层组
        return Promise.allSettled(
            baseServices.map(service =>
                this.addService(
                    service.serviceid,
                    service.servicetype,
                    service.serviceurl,
                    Function(
                        `return ${service.servicestyles || 'undefined'};`
                    )()
                )
            )
        )
            .then(vals => {
                vals.reverse().forEach(res => {
                    if (res.status === 'fulfilled') {
                        const layer = res.value
                        this.adapter.moveLayer(this.map, layer)
                        this.baseIds.push((layer as any).__layerId)
                    }
                })
            })
            .then(() => this.baseIds)
    }
}
